#pragma once
#include <iostream>
#include <pthread.h>
#include <queue>
#include <unistd.h>
#include "Mutex.hpp"
#include "Cond.hpp"

namespace BlockQueueModule
{
    // version 2
    using namespace CondModule;
    using namespace MutexModule;

    template<class T>
    class BlockQueue
    {
    private:
        bool IsFull() {return _cap == _q.size();}
        bool IsEmpty() {return _q.empty();}
        
    public:
        BlockQueue(int cap = 10)
            :_cap(cap), _cwait(0), _pwait(0)
        {}

        void Equeue(const T &data)   //生产者
        {
            LockGuard lockguard(_mutex);//RAII
            while (IsFull())
            {
                std::cout << "生产者进入等待..." << std::endl;
                _pwait++;
                //如果阻塞队列为满，则等待消费者发信号
                //wait首先解锁，让其他线程有访问临界区的机会
                _producter_signal.Wait(_mutex);
                _pwait--;
                std::cout << "生产者被唤醒..." << std::endl;
                //wait被唤醒后重新申请锁，访问临界区
            }

            //走到这里，说明生产者收到了消费者发的信号，临界区一定有空位置，可以生产了
            _q.push(data);

            //生产者放完数据，一定有数据，给消费者发信号可以消费了
            if (_cwait)
            {
                _consumer_signal.Notify();
            }
        }

        void Pop(T *data)         //消费者
        {
            LockGuard lockguard(_mutex);//RAII
            while (IsEmpty())
            {
                std::cout << "消费者进入等待..." << std::endl;
                _cwait++;
                //如果阻塞队列为空，则等待生产者发信号
                _consumer_signal.Wait(_mutex);
                _cwait--;
                std::cout << "消费者被唤醒..." << std::endl;
            }

            //走到这里，说明消费者收到了生产者发的信号，临界区一定有数据，可以消费了
            *data = _q.front();
            _q.pop();

            //消费者取完数据，一定有空位置，给生产者发信号可以生成了
            if (_pwait)
            {
                _producter_signal.Notify();
            }
        }

        ~BlockQueue()
        {}
    private:
        std::queue<T> _q;                  //临界资源
        int _cap;                          //默认临界区大小
        Mutex _mutex;                      //互斥锁
        Cond _producter_signal;            //生产者条件变量
        Cond _consumer_signal;             //消费者条件变量

        int _cwait;     //有多少个消费者在阻塞等待
        int _pwait;     //有多少个生产者在阻塞等待
    };

    // version 1
    // static const int gcap = 10; //默认临界区大小

    // template<class T>
    // class BlockQueue
    // {
    // private:
    //     bool IsFull()
    //     {
    //         return _cap == _q.size();
    //     }
        
    //     bool IsEmpty()
    //     {
    //         return _q.empty();
    //     }
    // public:
    //     BlockQueue(int cap = gcap)
    //         :_cap(cap), _cwait(0), _pwait(0)
    //     {
    //         pthread_mutex_init(&_mutex, nullptr);
    //         pthread_cond_init(&_producter_signal, nullptr);
    //         pthread_cond_init(&_consumer_signal, nullptr);
    //     }

    //     void Equeue(const T &data)   //生产者
    //     {
    //         pthread_mutex_lock(&_mutex);
    //         while (IsFull())
    //         {
    //             std::cout << "生产者进入等待..." << std::endl;
    //             _pwait++;
    //             //如果阻塞队列为满，则等待消费者发信号
    //             //wait首先解锁，让其他线程有访问临界区的机会
    //             pthread_cond_wait(&_producter_signal, &_mutex);
    //             _pwait--;
    //             std::cout << "生产者被唤醒..." << std::endl;
    //             //wait被唤醒后重新申请锁，访问临界区
    //         }

    //         //走到这里，说明生产者收到了消费者发的信号，临界区一定有空位置，可以生产了
    //         _q.push(data);

    //         //生产者放完数据，一定有数据，给消费者发信号可以消费了
    //         if (_cwait)
    //         {
    //             pthread_cond_signal(&_consumer_signal);
    //         }
    //         pthread_mutex_unlock(&_mutex);
    //     }

    //     void Pop(T *data)         //消费者
    //     {
    //         pthread_mutex_lock(&_mutex);
    //         while (IsEmpty())
    //         {
    //             std::cout << "消费者进入等待..." << std::endl;
    //             _cwait++;
    //             //如果阻塞队列为空，则等待生产者发信号
    //             pthread_cond_wait(&_consumer_signal, &_mutex);
    //             _cwait--;
    //             std::cout << "消费者被唤醒..." << std::endl;
    //         }

    //         //走到这里，说明消费者收到了生产者发的信号，临界区一定有数据，可以消费了
    //         *data = _q.front();
    //         _q.pop();

    //         //消费者取完数据，一定有空位置，给生产者发信号可以生成了
    //         if (_pwait)
    //         {
    //             pthread_cond_signal(&_producter_signal);
    //         }
    //         pthread_mutex_unlock(&_mutex);
    //     }

    //     ~BlockQueue()
    //     {
    //         pthread_mutex_destroy(&_mutex);
    //         pthread_cond_destroy(&_producter_signal);
    //         pthread_cond_destroy(&_consumer_signal);
    //     }
    // private:
    //     std::queue<T> _q;                  //临界资源
    //     int _cap;                          //最大容量
    //     pthread_mutex_t _mutex;            //互斥锁
    //     pthread_cond_t _producter_signal;  //生产者条件变量
    //     pthread_cond_t _consumer_signal;   //消费者条件变量

    //     int _cwait;     //有多少个消费者在阻塞等待
    //     int _pwait;     //有多少个生产者在阻塞等待
    // };
}